package cn.gson.financial.controller;

import cn.gson.financial.annotation.IgnoresLogin;
import cn.gson.financial.base.BaseCrudController;
import cn.gson.financial.common.SubjectExcelUtils;
import cn.gson.financial.kernel.controller.JsonResult;
import cn.gson.financial.kernel.exception.ServiceException;
import cn.gson.financial.kernel.model.entity.Subject;
import cn.gson.financial.kernel.model.entity.VoucherDetails;
import cn.gson.financial.kernel.model.vo.InitBlanceVo;
import cn.gson.financial.kernel.model.vo.RequestBlancceVo;
import cn.gson.financial.kernel.model.vo.SubjectListVo;
import cn.gson.financial.kernel.model.vo.SubjectVo;
import cn.gson.financial.kernel.service.SubjectService;
import cn.gson.financial.kernel.service.VoucherDetailsService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@RestController
@RequestMapping("/subject")
public class SubjectController extends BaseCrudController<SubjectService, Subject> {

    @Autowired
    private VoucherDetailsService voucherDetailsService;

    @Autowired
    private SubjectService subjectService;
    @Autowired
    private SubjectExcelUtils excelUtils;

    @DeleteMapping("/{id:\\d+}")
    @Transactional(rollbackFor = Throwable.class)
    public JsonResult delete(@PathVariable Long id) {
        try {
            QueryWrapper qw = Wrappers.query();
            qw.eq("id", id);
            this.setQwAccountSetsId(qw);
            subjectService.remove(qw);
            //删除期初数据
            QueryWrapper qc = Wrappers.query();
            qc.eq("subject_id", id);
            qc.isNull("voucher_id");
            this.setQwAccountSetsId(qw);
            voucherDetailsService.remove(qc);
            return JsonResult.successful();
        } catch (ServiceException se) {
            log.error("删除失败！", se);
            return JsonResult.failure(se.getMessage());
        } catch (Exception e) {
            log.error("删除失败！", e);
            return JsonResult.failure("删除失败！");
        }
    }

    @PostMapping
    @Transactional(rollbackFor = Throwable.class)
    public JsonResult save(@RequestBody Subject subject) {
        if (subject.getCurrencyAccounting().equals("") || !subject.getCurrencyAccounting().contains("{") || !subject.getCurrencyAccounting().contains("}")){
            subject.setCurrencyAccounting(null);
        }

        Integer parentId = subject.getParentId();
        QueryWrapper<VoucherDetails> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("subject_id",parentId);
        queryWrapper.isNull("voucher_id");
        List<VoucherDetails> list = voucherDetailsService.list(queryWrapper);

        try {
            this.setEntityAccountSetsId(subject);
            HashMap<String,String> save = subjectService.subjectSave(subject);
            if (save.get("false") != null ){
                return JsonResult.failure(save.get("false"));
            }
            if (list == null || list.size() == 0){
                //说明该科目下没有期初
            }else {
                //该科目下有期初，需要将该期初移到子科目下
                for (VoucherDetails voucherDetails : list) {
                    voucherDetails.setSubjectId(subject.getId());
                    if (subject.getAuxiliaryAccounting()==null || subject.getAuxiliaryAccounting().equals("")){
                        //没有辅助核算就是。科目名称就是  code-name格式
                        voucherDetails.setSubjectName(subject.getCode() + voucherDetails.getSubjectName().substring(voucherDetails.getSubjectName().indexOf("-")));
                    }else {
                        //有辅助核算的格式就是  subjectCode_auxiliaryCode-subjectName_auxiliaryName
                        voucherDetails.setSubjectName(subject.getCode() + voucherDetails.getSubjectName().substring(voucherDetails.getSubjectName().indexOf("_")));
                    }
                    voucherDetails.setSubjectCode(voucherDetails.getSubjectName().substring(0,voucherDetails.getSubjectName().indexOf("-")));
                    voucherDetailsService.updateQiChu(voucherDetails);
                }
            }
            //将父科目的辅助核算清除掉
            subjectService.updateSubject(subject.getParentId());//清空父科目的辅助核算
            return JsonResult.successful(save);
        } catch (Exception e) {
            log.error("创建失败！", e);
            return JsonResult.failure(e.getMessage());
        }
    }

    @Override
    public JsonResult list(@RequestParam Map<String, String> params) {
        QueryWrapper<Subject> qw = new QueryWrapper<>();
        qw.eq("ffs.account_sets_id", currentUser.getAccountSetsId());
        String keyWord = params.get("keyWord");//code和name模糊查询的参数（设置-科目）
        params.remove("keyWord");
        qw.allEq(params);
        if (!(keyWord == null) && !keyWord.equals("")){
            qw.and(wrapper -> wrapper.like("code",keyWord).or().like("name",keyWord));
        }
        List<SubjectVo> list = service.listVo(qw);
        //查询期初fxy_financial_init_blancevo该科目是否存在期初
        List<SubjectListVo> subjectListVos = new ArrayList<>();
        List<Integer> subjectIdList = new ArrayList<>();
        list.forEach(e->subjectIdList.add(e.getId()));
        List<VoucherDetails> voucherDetails = subjectService.findBySubId(subjectIdList);

        for (SubjectVo subjectVo : list) {
            SubjectListVo subjectListVo = new SubjectListVo();
            subjectListVo.setSubjectVo(subjectVo);
            subjectListVo.setQiChuHaveData(false);
            for (VoucherDetails voucherDetail : voucherDetails) {
                if (subjectVo.getId() == voucherDetail.getSubjectId() ||subjectVo.getId().equals(voucherDetail.getSubjectId())){
                    subjectListVo.setQiChuHaveData(true);
                    break;
                }
            }
            subjectListVos.add(subjectListVo);
        }
        return JsonResult.successful(subjectListVos);
    }

    /**
     * 凭证下拉数据
     *
     * @return
     */
//    @IgnoresLogin
    @RequestMapping("voucher/select")
    public JsonResult voucherSelect(@RequestParam(defaultValue = "0") boolean showAll) {
        QueryWrapper qw = Wrappers.query();
        this.setQwAccountSetsId(qw);
        List<SubjectVo> data = service.selectData(qw, showAll);
        return JsonResult.successful(data);
    }

    @RequestMapping("loadByCode")
    public JsonResult loadByCode(String[] code, Integer checkYear, Integer checkMonth, String name) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, checkYear);
        cal.set(Calendar.MONTH, checkMonth - 1);

        QueryWrapper qw = Wrappers.query();
        this.setQwAccountSetsId(qw);

        qw.and(wrapper -> {
            QueryWrapper qwe = (QueryWrapper) wrapper;
            for (String s : code) {
                qwe.or(true).likeRight("code", s);
            }
            if ("结转损益".equals(name)) {
                qwe.or(true).eq("type", "损益");
            }
            return wrapper;
        });

        List<Subject> subjects = service.list(qw);
        List<SubjectVo> vos = subjects.stream().distinct().collect(ArrayList::new, (list, source) -> {
            SubjectVo vo = new SubjectVo();
            BeanUtils.copyProperties(source, vo);
            list.add(vo);
        }, List::addAll);

        Map<String, SubjectVo> collect1 = vos.stream().distinct().collect(Collectors.toMap(SubjectVo::getCode, subjectVo -> subjectVo));

        for (String s : code) {
            SubjectVo sbj = collect1.get(s);
            if (sbj.getLevel() != 1) {
                this.recursiveParent(vos, sbj.getParentId());
            }
        }

        Map<Integer, SubjectVo> collect = vos.stream().distinct().collect(Collectors.toMap(SubjectVo::getId, subject -> subject));

        vos.forEach(subject -> {
            if (subject.getLevel() != 1) {
                SubjectVo parent = collect.get(subject.getParentId());
                parent.getChildren().add(subject);
            }
        });


        if (vos.size() > code.length) {
            List<SubjectVo> collect2 = vos.stream().filter(subjectVo -> subjectVo.getChildren().isEmpty()).collect(Collectors.toList());
            for (Subject subjectVo : collect2) {
                if (subjectVo.getLevel() != 1) {
                    this.recursiveChildren(collect, subjectVo, subjectVo.getParentId());
                }
            }
            vos = collect2;
        }

        List<SubjectVo> collect2 = vos.stream().sorted(Comparator.comparing(Subject::getCode)).distinct().collect(Collectors.toList());

        Set<String> codeSet = collect2.stream().collect(Collectors.mapping(subjectVo -> subjectVo.getCode(), Collectors.toSet()));

        Map<String, VoucherDetails> aggregateAmount = voucherDetailsService.getAggregateAmount(this.accountSetsId, codeSet, cal.getTime());

        Map<String, Object> data = new HashMap<>(2);
        data.put("subject", collect2);
        data.put("amount", aggregateAmount);

        return JsonResult.successful(data);
    }

    private void recursiveParent(List<SubjectVo> subjects, Integer parentId) {
        QueryWrapper qw = Wrappers.query();
        this.setQwAccountSetsId(qw);
        qw.eq("id", parentId);
        Subject parent = this.service.getOne(qw);
        SubjectVo vo = new SubjectVo();
        BeanUtils.copyProperties(parent, vo);
        subjects.add(vo);
        if (parent.getLevel() != 1) {
            this.recursiveParent(subjects, parent.getParentId());
        }
    }

    private void recursiveChildren(Map<Integer, SubjectVo> subjectMap, Subject subject, Integer parentId) {
        Subject parent = subjectMap.get(parentId);
        if (parent != null) {
            subject.setName(parent.getName() + "-" + subject.getName());
            if (parent.getLevel() != 1) {
                recursiveChildren(subjectMap, subject, parent.getParentId());
            }
        }
    }

    /**
     * 使用状态检查
     *
     * @param id
     * @return
     */
    @GetMapping("checkUse/{id}")
    public JsonResult checkUse(@PathVariable Integer id) {
        Boolean used = service.checkUse(id);
        return JsonResult.successful(used);
    }


    @PutMapping
    public JsonResult update(@RequestBody Subject subject) {
        if (subject.getCurrencyAccounting().equals("") || !subject.getCurrencyAccounting().contains("{") || !subject.getCurrencyAccounting().contains("}")){
            subject.setCurrencyAccounting(null);
        }
        this.setEntityAccountSetsId(subject);
        try {
            QueryWrapper qw = Wrappers.query();
            qw.eq("id", subject.getId());
            this.setQwAccountSetsId(qw);
            service.update(subject, qw);
            return JsonResult.successful("修改成功！");
        } catch (Exception e) {
            log.error("更新失败！", e);
            return JsonResult.failure(e.getMessage());
        }
    }

    /**
     * 科目余额
     *
     * @param subjectId
     * @return
     */
    @GetMapping("/balance")
    public JsonResult balance(Integer subjectId, Integer categoryId, Integer categoryDetailsId) {
        Double balance = service.balance(this.accountSetsId, subjectId, categoryId, categoryDetailsId);
        return JsonResult.successful(balance);
    }


    /**
     * 科目余额
     *
     * @param
     * @return
     */
    @PostMapping("/balance")
    @IgnoresLogin
    public JsonResult balance2(@RequestBody RequestBlancceVo vo) {
        log.info("/subject/balance入参："+vo);
        Double balance = service.balance2(this.accountSetsId, vo.getSubjectId(), vo.getAuxiliaryTitle(),vo.getCategoryVoList());
        return JsonResult.successful(balance);
    }

    /**
     * 导入
     *
     * @param multipartFile
     * @return
     */
    @PostMapping("/import")
    public JsonResult importVoucher(@RequestParam("file") MultipartFile multipartFile) {
        try {
            List<SubjectVo> voucherList = excelUtils.readExcel(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), this.currentUser);
            this.service.importVoucher(voucherList, this.currentUser.getAccountSets());
            return JsonResult.successful();
        } catch (ServiceException e) {
            return JsonResult.failure(e.getMessage());
        } catch (Exception e) {
            log.error("导入失败", e);
            throw new ServiceException("导入失败~", e);
        }
    }
}
